﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Net;

using log4net;

using CreativeMinds.DNSAPI.Core;

namespace CreativeMinds.DNSAPI.MSDNS {

	public class MSDNSAPI : DNSAPIBase {
		private readonly String serverName;
		private readonly String username;
		private readonly String password;

		public MSDNSAPI(ILog log, String serverName, String username, String password)
			: base(log) {
			this.serverName = serverName;
			this.username = username;
			this.password = password;
			this.Log.DebugFormat("MSDNSAPI initialized, server '{0}', user '{1}'", serverName, username);
		}

		#region Zone related public methods
		public override void CreatePrimaryZone(String zoneName, ZoneTransferType transfer, List<IPAddress> allowTransferTo, List<String> ns, Int32 ttl, Int32 expiryLimit, Int32 retryDelay, Int32 refreshInterval, Int32 minimumTTL, String soaStart, String responsiblePerson) {
			this.CreateZone(zoneName, ZoneType.Primary, null);

			String query = String.Format("SELECT * FROM MicrosoftDNS_NSType WHERE DomainName = \"{0}\"", zoneName);
			// Get the default created NS record(s)
			ManagementObjectCollection NSList = this.GetRecordsFromDns(query);
			foreach (ManagementObject oManObject in NSList) {
				// Remove the default created NS record(s)
				oManObject.Delete();
			}

			query = String.Format("SELECT * FROM MicrosoftDNS_SOAType WHERE DomainName = \"{0}\"", zoneName);
			// Get the SOA record, we need to update it!
			ManagementObjectCollection SOAList = this.GetRecordsFromDns(query);

			foreach (ManagementObject oManObject in SOAList) {
				Boolean propertyChange = false;
				String sProNames = "";
				foreach (PropertyData oPropData in oManObject.Properties) {
					sProNames += " " + oPropData.Name;
					if (oPropData.Name == "PrimaryServer" && (string)oPropData.Value != soaStart) {
						oPropData.Value = soaStart;
						propertyChange = true;
					}
					if (oPropData.Name == "ResponsibleParty" && (string)oPropData.Value != responsiblePerson) {
						oPropData.Value = responsiblePerson;
						propertyChange = true;
					}
					if (oPropData.Name == "ExpireLimit" && (UInt32)oPropData.Value != expiryLimit) {
						oPropData.Value = expiryLimit;
						propertyChange = true;
					}
					else if (oPropData.Name == "RetryDelay" && (UInt32)oPropData.Value != retryDelay) {
						oPropData.Value = retryDelay;
						propertyChange = true;
					}
					else if (oPropData.Name == "RefreshInterval" && (UInt32)oPropData.Value != refreshInterval) {
						oPropData.Value = refreshInterval;
						propertyChange = true;
					}
					else if (oPropData.Name == "MinimumTTL" && (UInt32)oPropData.Value != minimumTTL) {
						oPropData.Value = minimumTTL;
						propertyChange = true;
					}
					else if (oPropData.Name == "TTL" && (UInt32)oPropData.Value != ttl) {
						oPropData.Value = ttl;
						propertyChange = true;
					}
				}

				// Did we need to change any of the properties?
				if (propertyChange) {
					InvokeMethodOptions options = new InvokeMethodOptions();
					// Yes, let's save the changes!
					ManagementBaseObject outParams = oManObject.InvokeMethod("Modify", oManObject, options);
				}
			}

			foreach (String nameServer in ns) {
				this.CreateNSRecord(zoneName, zoneName, nameServer);
			}

			// TODO:
			//this.UpdateZoneTransfer(zone, transfer, allowTransferTo);

		}

		/// <summary>
		/// Create a new secondary zone on the server.
		/// </summary>
		/// <param name="zoneName">Name of the new zone.</param>
		/// <param name="primaries">The list of primary servers to synchronize against.</param>
		public override void CreateSecondaryZone(String zoneName, List<IPAddress> primaries) {
			this.CreateZone(zoneName, ZoneType.Secondary, primaries);
		}

		public override void ReadZone() {
			throw new NotImplementedException();
		}

		public override void UpdatePrimaryZone() {
			throw new NotImplementedException();
		}

		/// <summary>
		/// Update an existing secondary zone.
		/// </summary>
		/// <param name="zoneName">The name of the zone.</param>
		/// <param name="primaries">The list of primary servers to synchronize against.</param>
		public override void UpdateSecondaryZone(String zoneName, List<IPAddress> primaries) {
			ManagementObject zone = this.ReadMatchingZone(zoneName);
			if (zone != null) {
				// TODO:
			}
			else {
				this.Log.DebugFormat("Could not find the zone '{0}'", zoneName);
			}
		}

		/// <summary>
		/// Deletes the zone with the given name.
		/// </summary>
		/// <param name="zoneName">Name of the zone.</param>
		public override void DeleteZone(String zoneName) {
			this.Log.DebugFormat("DeleteZone called, zone: '{0}'", zoneName);
			ManagementObject zone = this.ReadMatchingZone(zoneName);
			if (zone != null) {
				zone.Delete();
				this.Log.DebugFormat("Zone '{0}' deleted", zoneName);
			}
			else {
				this.Log.WarnFormat("The zone '{0}' was not found, can not delete the zone.", zoneName);
			}
		}
		#endregion

		#region Record related public methods

		#region Create methods
		public override void CreateARecord(String zoneName, String domain, IPAddress address) {
			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("DnsServerName", this.serverName);
			parameters.Add("ContainerName", zoneName);
			parameters.Add("OwnerName", domain);
			parameters.Add("IPAddress", address.ToString());

			this.CreateRecord(zoneName, ResourceRecordType.A, parameters);
		}

		public override void CreateAAAARecord(String zoneName, String domain, IPAddress address) {
			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("DnsServerName", this.serverName);
			parameters.Add("ContainerName", zoneName);
			parameters.Add("OwnerName", domain);
			parameters.Add("IPv6Address", address.ToString());

			this.CreateRecord(zoneName, ResourceRecordType.AAAA, parameters);
		}

		public override void CreateNSRecord(String zoneName, String domain, String server) {
			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("DnsServerName", this.serverName);
			parameters.Add("ContainerName", zoneName);
			parameters.Add("OwnerName", domain);
			parameters.Add("NSHost", server);

			this.CreateRecord(zoneName, ResourceRecordType.NS, parameters);
		}

		public override void CreateCNAMERecord(String zoneName, String domain, String alias) {
			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("DnsServerName", this.serverName);
			parameters.Add("ContainerName", zoneName);
			parameters.Add("OwnerName", domain);
			parameters.Add("PrimaryName", alias);

			this.CreateRecord(zoneName, ResourceRecordType.CNAME, parameters);
		}

		public override void CreateMXRecord(String zoneName, String domain, String server, Int32 priority) {
			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("DnsServerName", this.serverName);
			parameters.Add("ContainerName", zoneName);
			parameters.Add("OwnerName", domain);
			parameters.Add("MailExchange", server);
			parameters.Add("Preference", (UInt16)priority);

			this.CreateRecord(zoneName, ResourceRecordType.MX, parameters);
		}

		public override void CreateSOARecord(String zoneName, String server, String responsiblePerson, Int32 expiryLimit, Int32 minimumTTL, Int32 refreshInterval, Int32 retryDelay, Int32 serialNumber) {
			throw new Exception("The method or operation is not implemented.");
		}

		public override void CreateRPRecord(String zoneName, String domain, String mailHost, String text) {
			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("DnsServerName", this.serverName);
			parameters.Add("ContainerName", zoneName);
			parameters.Add("OwnerName", domain);
			parameters.Add("RPMailbox", mailHost);
			parameters.Add("TXTDomainName", text);

			this.CreateRecord(zoneName, ResourceRecordType.RP, parameters);
		}

		public override void CreateMBRecord(String zoneName, String domain, String server) {
			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("DnsServerName", this.serverName);
			parameters.Add("ContainerName", zoneName);
			parameters.Add("OwnerName", domain);
			parameters.Add("MBHost", server);

			this.CreateRecord(zoneName, ResourceRecordType.MB, parameters);
		}

		public override void CreateTXTRecord(String zoneName, String domain, String text) {
			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("DnsServerName", this.serverName);
			parameters.Add("ContainerName", zoneName);
			parameters.Add("OwnerName", domain);
			parameters.Add("DescriptiveText", text);

			this.CreateRecord(zoneName, ResourceRecordType.TXT, parameters);
		}
		#endregion

		// TODO: READ?!

		#region Update
		public override void UpdateARecord(String zoneName, String domain, IPAddress address) {
			ManagementObject record = this.ReadUniqueRecord(zoneName, ResourceRecordType.A, domain);

			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("IPAddress", address.ToString());

			this.UpdateRecord(zoneName, ResourceRecordType.A, record, parameters);
		}

		public override void UpdateAAAARecord(String zoneName, String domain, IPAddress address) {
			ManagementObject record = this.ReadUniqueRecord(zoneName, ResourceRecordType.AAAA, domain);

			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("IPAddress", address.ToString());

			this.UpdateRecord(zoneName, ResourceRecordType.AAAA, record, parameters);
		}

		public override void UpdateCNAMERecord(String zoneName, String domain, String alias) {
			ManagementObject record = this.ReadUniqueRecord(zoneName, ResourceRecordType.CNAME, domain);

			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("PrimaryName", alias);

			this.UpdateRecord(zoneName, ResourceRecordType.CNAME, record, parameters);
		}

		public override void UpdateMBRecord(String zoneName, String domain, String mailHost) {
			ManagementObject record = this.ReadUniqueRecord(zoneName, ResourceRecordType.MB, domain);

			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("MBHost", mailHost);

			this.UpdateRecord(zoneName, ResourceRecordType.MB, record, parameters);
		}

		public override void UpdateSOARecord(String zoneName, String server, String responsiblePerson, Int32 expiryLimit, Int32 minimumTTL, Int32 refreshInterval, Int32 retryDelay, Int32 serialNumber) {
			ManagementObject soaRecord = this.ReadSOARecord(zoneName);

			Dictionary<String, Object> parameters = new Dictionary<String, Object>();
			parameters.Add("PrimaryServer", server);
			parameters.Add("ResponsibleParty", responsiblePerson);
			parameters.Add("ExpireLimit", (UInt32)expiryLimit);
			parameters.Add("RetryDelay", (UInt32)retryDelay);
			parameters.Add("RefreshInterval", (UInt32)refreshInterval);
			parameters.Add("MinimumTTL", (UInt32)minimumTTL);
			// TODO:
			//parameters.Add("SerialNumber", CalculateNewVersion((UInt32)soaRecord["SerialNumber"]));
			//parameters.Add("TTL", (UInt32)tll);
			// TODO:

			this.UpdateRecord(zoneName, ResourceRecordType.SOA, soaRecord, parameters);
		}

		public override void UpdateMXRecord(String zoneName, String domain, String oldHost, String newHost, Int32 oldPriority, Int32 newPriority) {
			String queryString = String.Format("SELECT * FROM MicrosoftDNS_MXType WHERE OwnerName = \"{0}\" AND ContainerName=\"{2}\" AND MailExchange = \"{1}\" AND Preference = \"{3}\"", zoneName, oldHost, domain, oldPriority);
			ManagementObject record = this.ReadRecord(queryString);

			if (record != null) {
				Dictionary<String, Object> parameters = new Dictionary<String, Object>();
				parameters.Add("Preference", (UInt16)newPriority);
				parameters.Add("MailExchange", newHost);
				this.UpdateRecord(zoneName, ResourceRecordType.MX, record, parameters);
			}
			else {
				// TODO:
			}
		}

		public override void UpdateTXTRecord(String zoneName, String domain, String oldText, String newText) {
			String queryString = String.Format("SELECT * FROM MicrosoftDNS_TXTType WHERE OwnerName = \"{0}\" AND ContainerName = \"{2}\" AND DescriptiveText = \"{1}\"", zoneName, oldText.Replace("\"", ""), domain);
			ManagementObject record = this.ReadRecord(queryString);

			if (record != null) {
				Dictionary<String, Object> parameters = new Dictionary<String, Object>();
				parameters.Add("DescriptiveText", newText);
				this.UpdateRecord(zoneName, ResourceRecordType.TXT, record, parameters);
			}
			else {
			}
		}

		// TODO: Update record for RP
		#endregion

		#region Delete methods
		public override void DeleteARecord(String zoneName, String domain) {
			this.DeleteUniqueRecord(zoneName, domain, ResourceRecordType.A);
		}

		public override void DeleteAAAARecord(String zoneName, String domain) {
			this.DeleteUniqueRecord(zoneName, domain, ResourceRecordType.AAAA);
		}

		public override void DeleteCNAMERecord(String zoneName, String domain) {
			this.DeleteUniqueRecord(zoneName, domain, ResourceRecordType.CNAME);
		}

		public override void DeleteNSRecord(String zoneName, String domain, String server) {
			throw new Exception("The method or operation is not implemented.");
		}

		public override void DeleteMBRecord(String zoneName, String domain) {
			this.DeleteUniqueRecord(zoneName, domain, ResourceRecordType.MB);
		}

		public override void DeleteRPRecord(String zoneName, String domain, String mailHost) {
			String queryString = String.Format("SELECT * FROM MicrosoftDNS_RPType WHERE OwnerName = \"{0}\" AND ContainerName=\"{2}\" AND RPMailbox = \"{1}\"", zoneName, mailHost, domain);
			ManagementObject record = this.ReadRecord(queryString);

			if (record != null) {
				record.Delete();
			}
			else {
			}
		}

		public override void DeleteMXRecord(String zoneName, String domain, String mailHost, Int32 priority) {
			String queryString = String.Format("SELECT * FROM MicrosoftDNS_MXType WHERE OwnerName = \"{0}\" AND ContainerName=\"{2}\" AND MailExchange = \"{1}\" AND Preference = \"{3}\"", zoneName, mailHost, domain, priority);
			ManagementObject record = this.ReadRecord(queryString);

			if (record != null) {
				record.Delete();
			}
			else {
			}
		}

		public override void DeleteTXTRecord(String zoneName, String domain, String text) {
			String queryString = String.Format("SELECT * FROM MicrosoftDNS_TXTType WHERE OwnerName = \"{0}\" AND ContainerName = \"{2}\" AND DescriptiveText = \"{1}\"", zoneName, text.Replace("\"", ""), domain);
			ManagementObject record = this.ReadRecord(queryString);

			if (record != null) {
				record.Delete();
			}
			else {
			}
		}

		public override void DeleteSOARecord(String zoneName) {
			throw new Exception("The method or operation is not implemented.");
		}

		public override void DeleteAllTXTRecords(String zoneName) {
			String queryString = String.Format("SELECT * FROM MicrosoftDNS_TXTType WHERE OwnerName = \"{0}\"", zoneName);
			ManagementObjectCollection records = this.GetRecordsFromDns(queryString);

			foreach (ManagementObject record in records) {
				record.Delete();
			}
		}

		public override void DeleteAllMXRecords(String zoneName) {
			String queryString = String.Format("SELECT * FROM MicrosoftDNS_MXType WHERE OwnerName = \"{0}\"", zoneName);
			ManagementObjectCollection records = this.GetRecordsFromDns(queryString);

			foreach (ManagementObject record in records) {
				record.Delete();
			}
		}
		#endregion

		#endregion

		#region Private methods
		/// <summary>
		/// Get the name of the server the application is running on.
		/// </summary>
		/// <returns>The name of the computer the application is running on.</returns>
		private String GetLocalServerName() {
			return Environment.MachineName;
		}

		/// <summary>
		/// Create a WMI connection to the given server.
		/// </summary>
		/// <param name="server">The name of the server we want to connect to.</param>
		/// <returns>A fully initialized <c ref="System.Management.ConnectionOptions">ConnectionOptions</c> object to the given server.</returns>
		private ConnectionOptions GetConnection(String server) {
			String localMachine = this.GetLocalServerName();

			ConnectionOptions conOptions = new ConnectionOptions();
			conOptions.Authentication = AuthenticationLevel.Packet;
			conOptions.Impersonation = ImpersonationLevel.Impersonate;

			// If not connecting to the local machine, we need a username/password
			if (localMachine.ToLower() != server.ToLower()) {
			    if (String.IsNullOrWhiteSpace(this.username) || String.IsNullOrWhiteSpace(this.password)) {
			        // TODO:
			        throw new ApplicationException("missing username/password");
			    }
			    conOptions.Username = this.username;
			    conOptions.Password = this.password;
			}

			return conOptions;
		}

		/// <summary>
		/// Get the WMI path to the DNS server on the computer with the given name.
		/// </summary>
		/// <param name="serverName">The name of the server.</param>
		/// <returns>The WMI path to the DNS server.</returns>
		private string GetDNSPath(String serverName) {
			return "\\\\" + serverName + "\\root\\microsoftdns";
		}

		private ManagementObject ReadMatchingZone(String zoneName) {
			ConnectionOptions conOptions = this.GetConnection(this.serverName);

			String serverWMIPath = this.GetDNSPath(this.serverName);
			ManagementScope scope = new ManagementScope(serverWMIPath, conOptions);

			// Explict connection to WMI namespace
			scope.Connect();

			ManagementObjectSearcher query = new ManagementObjectSearcher(String.Format("SELECT * FROM MicrosoftDNS_Zone WHERE Name = \"{0}\"", zoneName));

			// Use connection for this query
			query.Scope = scope;

			ManagementObjectCollection resultCollection = query.Get();
			if (resultCollection.Count != 1) {
				this.Log.WarnFormat("One zone was expected for '{0}', but {1} was found", zoneName, resultCollection.Count);
				// TODO:
				throw new ApplicationException(String.Format("Exactly one zone expected in the result, got {0}.", resultCollection.Count));
			}
			ManagementObject record = null;
			foreach (ManagementObject man in resultCollection) {
				record = man;
				break;
			}

			return record;
		}

		/// <summary>
		/// Create a new zone on the DNS server.
		/// </summary>
		/// <param name="zoneName">The name of the zone.</param>
		/// <param name="type">The type of zone that needs to be created.</param>
		/// <param name="primaries">A list of primary servers, or null if creating a Primary zone.</param>
		private void CreateZone(String zoneName, ZoneType type, List<IPAddress> primaries) {
			ManagementClass domain = this.GetClass("MicrosoftDNS_Zone");

			ManagementBaseObject inputParams = domain.GetMethodParameters("CreateZone");
			inputParams["ZoneName"] = zoneName;
			inputParams["ZoneType"] = (UInt32)type;
			inputParams["DsIntegrated"] = false;
			if (type == ZoneType.Secondary && primaries != null && primaries.Count > 0) {
				IEnumerable<String> ipStrings = primaries.Select(p => p.ToString());

				inputParams["IpAddr"] = ipStrings.ToArray();
			}

			this.Log.DebugFormat("Creating zone '{0}' on server '{1}'", zoneName, this.GetLocalServerName());
			ManagementBaseObject outParams = domain.InvokeMethod("CreateZone", inputParams, null);
			this.Log.DebugFormat("Zone '{0}' created.", zoneName);
		}

		private ManagementClass GetClass(String className) {
			ConnectionOptions conOptions = this.GetConnection(this.serverName);

			String path = this.GetDNSPath(this.serverName) + ":" + className;
			ManagementScope scope = new ManagementScope(path, conOptions);

			// Explict connection to WMI namespace
			scope.Connect();

			this.Log.DebugFormat("Getting class '{0}'", path);
			ManagementPath manPath = new ManagementPath(path);

			return new ManagementClass(scope, manPath, null);
		}

		private ManagementObjectCollection GetRecordsFromDns(String query) {
			ConnectionOptions conOptions = this.GetConnection(this.serverName);

			String path = this.GetDNSPath(this.serverName);
			ManagementScope scope = new ManagementScope(path, conOptions);

			// Explict connection to WMI namespace
			scope.Connect();

			ManagementObjectSearcher queryObj = new ManagementObjectSearcher(query);

			// Use connection for this query
			queryObj.Scope = scope;

			return queryObj.Get();
		}

		private void CreateRecord(String zoneName, ResourceRecordType type, Dictionary<String, Object> parameters) {
			String resourceType = String.Empty;
			switch (type) {
				case ResourceRecordType.A:
					resourceType = "MicrosoftDNS_AType";
					break;
				case ResourceRecordType.AAAA:
					resourceType = "MicrosoftDNS_AAAAType";
					break;
				case ResourceRecordType.CNAME:
					resourceType = "MicrosoftDNS_CNAMEType";
					break;
				case ResourceRecordType.MX:
					resourceType = "MicrosoftDNS_MXType";
					break;
				case ResourceRecordType.NS:
					resourceType = "MicrosoftDNS_NSType";
					break;
				case ResourceRecordType.MB:
					resourceType = "MicrosoftDNS_MBType";
					break;
				case ResourceRecordType.RP:
					resourceType = "MicrosoftDNS_RPType";
					break;
				case ResourceRecordType.TXT:
					resourceType = "MicrosoftDNS_TXTType";
					break;
				default:
					throw new ApplicationException(String.Format("Type {0} not defined in CreateRecord yet.", type));
			}

			ManagementClass recordClass = this.GetClass(resourceType);
			ManagementBaseObject inputParams = recordClass.GetMethodParameters("CreateInstanceFromPropertyData");
			foreach (KeyValuePair<String, Object> pair in parameters) {
				inputParams[pair.Key] = pair.Value;
			}

			this.Log.DebugFormat("Creating '{1}' in zone '{0}'", zoneName, type);
			ManagementBaseObject outParams = recordClass.InvokeMethod("CreateInstanceFromPropertyData", inputParams, null);
			this.Log.DebugFormat("Record type '{0}' created in zone '{1}'.", type, zoneName);

			// TODO:
			//this.IncreaseSOAVersion(zone);
		}

		private void DeleteUniqueRecord(String zoneName, String domain, ResourceRecordType type) {
			ManagementObject record = this.ReadUniqueRecord(zoneName, type, domain);

			if (record != null) {
				//this.WriteToLog(String.Format("Deleting record {1} on domain {0} of type {2}", zone, domain, type));
				record.Delete();

				// TODO:
				//this.IncreaseSOAVersion(zone);
			}
			else {
			}
		}

		private ManagementObject ReadRecord(String queryString) {
			ConnectionOptions conOptions = this.GetConnection(this.serverName);

			String serverWMIPath = this.GetDNSPath(this.serverName);
			ManagementScope scope = new ManagementScope(serverWMIPath, conOptions);

			// Explict connection to WMI namespace
			scope.Connect();

			ManagementObjectSearcher query = new ManagementObjectSearcher(queryString);

			// Use connection for this query
			query.Scope = scope;

			ManagementObjectCollection resultCollection = query.Get();
			if (resultCollection.Count != 1) {
				// TODO:
				throw new ApplicationException(String.Format("Exactly one record expected in the result, got {0}.", resultCollection.Count));
			}
			ManagementObject record = null;
			foreach (ManagementObject man in resultCollection) {
				record = man;
				break;
			}
			return record;
		}

		private ManagementObject ReadUniqueRecord(String zoneName, ResourceRecordType type, String domain) {
			String queryString = "SELECT * FROM {0} WHERE OwnerName = \"{1}\" AND {2} = \"{3}\"";
			String recordTypeString = "";
			switch (type) {
				case ResourceRecordType.A:
					recordTypeString = "MicrosoftDNS_AType";
					break;
				case ResourceRecordType.AAAA:
					recordTypeString = "MicrosoftDNS_AAAAType";
					break;
				case ResourceRecordType.CNAME:
					recordTypeString = "MicrosoftDNS_CNAMEType";
					break;
				case ResourceRecordType.MB:
					recordTypeString = "MicrosoftDNS_MBType";
					break;
				// TODO: More!?

				default:
					throw new ApplicationException("Unknown record type.");
			}

			queryString = String.Format(queryString, recordTypeString, zoneName, "OwnerName", domain);
			return this.ReadRecord(queryString);
		}

		private ManagementObject ReadSOARecord(String zoneName) {
			ConnectionOptions conOptions = this.GetConnection(this.serverName);

			String serverWMIPath = this.GetDNSPath(this.serverName);
			ManagementScope scope = new ManagementScope(serverWMIPath, conOptions);

			// Explict connection to WMI namespace
			scope.Connect();

			ManagementObjectSearcher query = new ManagementObjectSearcher("SELECT * FROM MicrosoftDNS_SOAType WHERE OwnerName = \"" + zoneName + "\"");

			// Use connection for this query
			query.Scope = scope;

			ManagementObjectCollection resultCollection = query.Get();
			if (resultCollection.Count != 1) {
				// TODO:
				throw new ApplicationException(String.Format("Exactly one record expected in the result, got {0}.", resultCollection.Count));
			}
			ManagementObject record = null;
			foreach (ManagementObject man in resultCollection) {
				record = man;
				break;
			}
			return record;
		}

		private void UpdateRecord(String zoneName, ResourceRecordType type, ManagementObject record, Dictionary<String, Object> parameters) {
			Boolean recordChanged = false;
			foreach (KeyValuePair<String, Object> pair in parameters) {
				if (pair.Value.GetType() == Type.GetType("System.String")) {
					if ((String)record[pair.Key] != (String)pair.Value) {
						record[pair.Key] = (String)pair.Value;
						recordChanged = true;
					}
				}
				if (pair.Value.GetType() == Type.GetType("System.UInt16")) {
					if ((UInt16)record[pair.Key] != (UInt16)pair.Value) {
						record[pair.Key] = (UInt16)pair.Value;
						recordChanged = true;
					}
				}
				if (pair.Value.GetType() == Type.GetType("System.UInt32")) {
					if ((UInt32)record[pair.Key] != (UInt32)pair.Value) {
						record[pair.Key] = (UInt32)pair.Value;
						recordChanged = true;
					}
				}
			}

			if (recordChanged) {
				InvokeMethodOptions options = new InvokeMethodOptions();
				this.Log.DebugFormat("Updating record {1} on domain {0}", zoneName, type);
				ManagementBaseObject outParams = record.InvokeMethod("modify", record, options);
				this.Log.DebugFormat("Record {1} on domain {0} updated.", zoneName, type);
			}

			if (type != ResourceRecordType.SOA) {
				// TODO:
				//this.IncreaseSOAVersion(zone);
			}
		}
		#endregion
	}
}